package com.vpa.graphics;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.Map;
import java.util.Map.Entry;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.RelativeLayout;

public class GLView extends GLSurfaceView implements GLSurfaceView.Renderer
{

	int cheight, cwidth;
	Context c;
	boolean editMode = false;
	boolean goGreen = false;
	GraphicsController controller;
	Thread main;
	Texture trash;
	int trashid = -1;
	protected Button plus, minus, okay;
	private RelativeLayout parent;
	boolean setskip = false;
	private Runnable cachedEnable = new Runnable(){
		public void run()
		{
			minus.setEnabled(true);
        	plus.setEnabled(true);
        	okay.setEnabled(true);
		}
	};
	Handler h;
	
	public GLView(Context context) 
	{
		super(context);
		//DO_SOMETHING!
	}
	public GLView(Context c, AttributeSet att)
	{
		super(c, att);
		this.c = c;
		main = Thread.currentThread();
		
	}
	
	public void register(GraphicsController ctl) throws ThreadedGraphicsException
	{
		plus = new Button(c);
		plus.setText("+");
		minus = new Button(c);
		minus.setText("-");
		okay = new Button(c);
		okay.setText("Done");
		
		plus.setVisibility(View.INVISIBLE);
		minus.setVisibility(View.INVISIBLE);
		okay.setVisibility(View.INVISIBLE);
		h = new Handler();
		
		
		if(main != Thread.currentThread())
			throw new ThreadedGraphicsException("You must create the Graphics Controller from the main (graphics) thread");
		controller = ctl;
		trashid = controller.addImageSpot(com.vpa.R.drawable.trash);
		setZOrderOnTop(true);
		setEGLConfigChooser(8, 8, 8, 8, 16, 0);
        getHolder().setFormat(PixelFormat.TRANSLUCENT);
		setRenderer(this);
		setRenderMode(RENDERMODE_CONTINUOUSLY);
		setWillNotDraw(false);
		TouchListener tl;
		setOnTouchListener(tl = new TouchListener(this));
		setOnLongClickListener(tl);
		
		plus.setOnClickListener(tl);
		minus.setOnClickListener(tl);
		okay.setOnClickListener(tl);
	}
	
	/*@Override
	public void onDraw(Canvas context)
	{
		if(!willNotDraw() && context != null)
		{
			for(Map.Entry<Integer, Spot> e : controller.spots.entrySet())
	        {
	        	Spot s = e.getValue();
	        }
			p.setColor(Color.BLACK);
			p.setTypeface(Typeface.DEFAULT_BOLD);
			p.setTextSize(16);
			//context.drawText("HELLO", 500, 500, p);
		}
	}*/
	
	public void onDrawFrame(GL10 gl) 
	{
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);// clear Screen and Depth Buffer
        gl.glLoadIdentity();// Reset the Matrix
        //gl.glTranslatef(0.0f, 0.0f, -((float)cheight)/(.4142f*(float)cwidth));     //note .4142=tan(45/2deg)
        for(Map.Entry<Integer, Spot> e : controller.spots.entrySet())
        {
        	Spot s = e.getValue();
        	if(!s.loaded && s.drawables != null && s.readyToLoad)
        	{
        		boolean fail = false;
        		for(int i = 0; i < s.drawables.length; i++)
        		{
        			s.move(s.x, s.y);
        			if(s.drawables[i] != null && !s.drawables[i].loadGLTexture(gl, c))
        				fail = true;
        		}
        		if(!fail)
        			s.loaded = true;
        	}
        	gl.glPushMatrix();
        	if(e.getKey().intValue() != trashid)
        		s.draw(gl, this);
        	gl.glPopMatrix();
        }
        if(editMode)
        	controller.spots.get(trashid).draw(gl, this);
        if(!plus.isEnabled() && !setskip)
        {
        	h.post(cachedEnable);
        }
        if(setskip)
        	setskip = false;
    }

    public void onSurfaceChanged(GL10 gl, int width, int height) {
        if(height == 0) {                       //Prevent A Divide By Zero By
            height = 1;                         //Making Height Equal One
        }
        
        gl.glViewport(0, 0, width, height);     //Reset The Current Viewport
        gl.glMatrixMode(GL10.GL_PROJECTION);    //Select The Projection Matrix
        gl.glLoadIdentity();                    //Reset The Projection Matrix
        
       //Calculate The Aspect Ratio Of The Window
        //GLU.gluPerspective(gl, 45.0f, (float)width / (float)height, 0.1f, 100.0f);
        gl.glOrthof(                                    // Set Ortho Projection (Left,Right,Bottom,Top,Front,Back)
                0, width,
                0, height,
                1.0f, -1.0f
             );
        
        cwidth = width;
        cheight = height;
        
        h.post(new Runnable(){
        	public void run()
        	{
        		parent = (RelativeLayout)getParent();
                parent.removeView(okay);
                parent.removeView(plus);
                parent.removeView(minus);
                
        		RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(100,  50);
        		params.setMargins(cwidth - 105, cheight - 55, 0, 0);
        		parent.addView(okay, params);
        		params = new RelativeLayout.LayoutParams(100, 50);
        		params.setMargins(cwidth - 210, cheight - 55, 0, 0);
        		parent.addView(plus, params);
        		params = new RelativeLayout.LayoutParams(100, 50);
        		params.setMargins(cwidth - 315, cheight - 55, 0, 0);
        		parent.addView(minus, params);
        	}
        });
        
        gl.glMatrixMode(GL10.GL_MODELVIEW);     //Select The Modelview Matrix
        gl.glLoadIdentity();                   //Reset The Modelview Matrix
        
        if(trashid != -1)
        {
        	Spot s = controller.spots.get(trashid);
        	float r = s.getHeight()/(float)s.getWidth();
        	if(height > width)
        	{
        		int w = width/6;
        		int h = (int)(w * r);
        		s.setDims(w, h, false);
        	}
        	else
        	{
        		int h = height/6;
        		int w = (int)(h / r);
        		s.setDims(w, h, false);
        	}
        	
        	s.x = (int)(.05f*width);
        	s.y = (int)(.95f*height) - s.getHeight();
        }
    }
 
    public void onSurfaceCreated(GL10 gl, EGLConfig config) 
    {	
    	gl.glEnable(GL10.GL_TEXTURE_2D);            //Enable Texture Mapping ( NEW )
    	gl.glEnable(GL10.GL_BLEND);
    	gl.glShadeModel(GL10.GL_SMOOTH);            //Enable Smooth Shading
    	gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);    //Black Background
    	gl.glClearDepthf(1.0f);                     //Depth Buffer Setup
    	gl.glEnable(GL10.GL_DEPTH_TEST);            //Enables Depth Testing
    	gl.glDepthFunc(GL10.GL_LEQUAL);             //The Type Of Depth Testing To Do
    	
    	//Really Nice Perspective Calculations
    	gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
    }
    public void activateButtons()
    {
    	okay.setVisibility(VISIBLE);
    	minus.setVisibility(VISIBLE);
    	plus.setVisibility(VISIBLE);
    }
    public void deactivateButtons()
    {
    	okay.setVisibility(INVISIBLE);
    	minus.setVisibility(INVISIBLE);
    	plus.setVisibility(INVISIBLE);
    }
}

class Texture {

    private FloatBuffer texbuf;   // buffer holding the vertices
    private FloatBuffer vertexbuf;   // buffer holding the vertices
    
    public boolean suggestMove = false;
    ResourceBundle res; 
    int x, y, h, w;
    Spot s;
    GLView v;
    private float tex_vertices[] = {
    		// Mapping coordinates for the vertices
    		0.207f, 1-.207f,     // top left     (V2)
    		0.207f, 0.207f,     // bottom left  (V1)
    		1-.207f, 1-.207f,     // top right    (V4)
    		1-.207f, 0.207f      // bottom right (V3)
    };
    public float vertices[] = {
    		0f, 0f,  0.0f,        // V.234 - bottom left
    		0f,  400f,  0.0f,        // V2 - top left
    		400f, 0f,  0.0f,        // V3 - bottom right
    		400f,  400f,  0.0f         // V4 - top right
    };
 
    public Texture(ResourceBundle im, GLView v)
    {
    	this.v = v;
    	float px = (1- im.width/(float)im.fdim)/2;
    	float py = (1 - im.height/(float)im.fdim)/2;
    	tex_vertices[0] = tex_vertices[2] = px; //lefts
    	tex_vertices[3] = tex_vertices[7] = py; //bottoms
    	tex_vertices[1] = tex_vertices[5] = 1 - py; //tops
    	tex_vertices[4] = tex_vertices[6] = 1-px; //rights
    	
    	res = im;
    	
        ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(tex_vertices.length * 4);
        vertexByteBuffer.order(ByteOrder.nativeOrder());
        texbuf = vertexByteBuffer.asFloatBuffer();
        texbuf.put(tex_vertices);
        texbuf.position(0);
        
        resetVertices();
    }
    private void resetVertices()
    {
    	ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(vertices.length * 4);
        vertexByteBuffer.order(ByteOrder.nativeOrder());
        vertexbuf = vertexByteBuffer.asFloatBuffer();
        vertexbuf.put(vertices);
        vertexbuf.position(0);
    }
    private int[] textures = new int[1];
    
    private void moveTo(int x, int y, int w, int h)
    {
    	vertices[1] = vertices[7] = v.cheight - y - h;
		vertices[4] = vertices[10] = v.cheight - y; //set y of tops
		vertices[6] = vertices[9] = w + x; //set x of right
		vertices[0] = vertices[3] = x; //set x of lefts
		vertexbuf.clear();
		vertexbuf.put(vertices);
		vertexbuf.position(0);
    }
    public void move(int x, int y, int w, int h)
    {
	    this.x = x;
	    this.y = y;
	    this.w = w;
	    this.h = h;
	    suggestMove = true;
    }
    public boolean loadGLTexture(GL10 gl, Context context)
    {
    	if(res == null || res.id == 0)
    		return false;
    	InputStream is = null;
    	if(res.id > 0)
    		is = context.getResources().openRawResource(res.id);
    	Bitmap bitmap;
    	if(res.id == -1)
    		bitmap = res.b;
    	else
    		bitmap = BitmapFactory.decodeStream(is);
	    
	    // generate one texture pointer
	    gl.glGenTextures(1, textures, 0);
	    // ...and bind it to our array
	    gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
	    
	    // create nearest filtered texture
	    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
	    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
	    
	    // Use Android GLUtils to specify a two-dimensional texture image from our bitmap
	    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
	    
	    // Clean up
	    if(res.id != -1)
	    	bitmap.recycle();
    	return true;
    }
    public void draw(GL10 gl)
    {
    	if(suggestMove)
    	{
    		moveTo(x, y, w, h);
    		suggestMove = false;
    	}
    	gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
    	
    	// Point to our buffers
    	gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
    	gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    	
    	// Set the face rotation
    	gl.glFrontFace(GL10.GL_CW);
    	
    	// Point to our vertex buffer
    	gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexbuf);
    	gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texbuf);
    	gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);
    	// Draw the vertices as triangle strip
    	gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
    	
    	//Disable the client state before leaving
    	gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
    	gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
    }
}

class TouchListener implements View.OnTouchListener, View.OnLongClickListener, View.OnClickListener
{
	GLView gl;
	Spot cmover, trash = null;
	int cmover_id;
	int mpadx, mpady;
	boolean lc;
	
	public TouchListener(GLView gl)
	{
		this.gl = gl;
		trash = gl.controller.spots.get(gl.trashid);
	}
	@Override
	public boolean onTouch(View arg0, MotionEvent eve) 
	{
		if(MotionEvent.ACTION_DOWN == eve.getAction())
		{
			for(Entry<Integer, Spot> e : gl.controller.spots.entrySet())
			{
				Spot s = e.getValue();
				if(s.x < eve.getX() && s.x + s.getWidth() > eve.getX() && 
						s.y < eve.getY() && s.y + s.getHeight() > eve.getY())
				{
					mpadx = (int)eve.getX() - s.x;
					mpady = (int)eve.getY() - s.y;
					if(cmover != null && cmover != s)
						cmover.moving = false;
					cmover = s;
					cmover_id = e.getKey().intValue();
				}
			}
		}
		else if(MotionEvent.ACTION_MOVE == eve.getAction() && lc)
		{
			if(cmover != null)
			{
				cmover.moving = true;
				cmover.move((int)eve.getX() - mpadx, (int)eve.getY() - mpady);
				if(trash.x < eve.getX() && trash.x + trash.getWidth() > eve.getX() &&
						trash.y < eve.getY() && trash.y + trash.getHeight() > eve.getY())
				{
					gl.goGreen = true;
					cmover.overTrash = true;
				}
				else
				{
					cmover.overTrash = false;
					gl.goGreen = false;
				}
			}
		}
		else if(MotionEvent.ACTION_UP == eve.getAction() && lc)
		{
			if(gl.goGreen == true)
			{
				try
				{
					gl.controller.removeSpot(cmover_id);	
				}
				catch(ThreadedGraphicsException e)
				{}
				gl.editMode = false;
				gl.goGreen = false;
				gl.deactivateButtons();
			}
		}
		return false;
	}
	
	@Override
	public boolean onLongClick(View v) 
	{
		if(cmover != null)
		{
			lc = true;
			gl.editMode = true;
			gl.activateButtons();
		}
		return false;
	}
	
	@Override
	public void onClick(View v) 
	{
		if(cmover != null)
		{
			if(v == gl.plus)
			{
				int h = cmover.getHeight() + 20;
				int w = (int)(h * cmover.originalAspRat);
				cmover.setDims(w, h, true);
				if(cmover.type == 1)
				{
					((DigitalGauge)cmover).text_size += 5;
				}
				cmover.move(cmover.x, cmover.y);
			}
			else if(v == gl.minus)
			{
				int h = cmover.getHeight() - 20;
				int w = (int)(h * cmover.originalAspRat);
				cmover.setDims(w, h, false);
				cmover.move(cmover.x, cmover.y);
			}
			else if(v == gl.okay)
			{
				gl.deactivateButtons();
				
				cmover.overTrash = false;
				cmover.moving = false;
				cmover = null;
				lc = false;
				gl.editMode = false;
				gl.goGreen = false;
				return;
			}
			gl.plus.setEnabled(false);
			gl.minus.setEnabled(false);
			gl.okay.setEnabled(false);
			gl.setskip = true;
		}
	}
}
